﻿using System;
using Microsoft.Xrm.Sdk;
using System.Fakes;
using Microsoft.Xrm.Sdk.Fakes;

namespace Xrm.Framework.Test.Unit.Fakes
{
    /// <summary>
    /// Base Class for all Dynamics CRM Plugin Unit Tests using Fakes
    /// </summary>
    public abstract class PluginUnitTest : Unit.PluginUnitTest
    {
        #region Instance Variables

        private readonly Guid _stubBusinessUnitId = Guid.NewGuid();
        private readonly Guid _stubContextUserId = Guid.NewGuid();
        private readonly Guid _stubCorrelationId = Guid.NewGuid();
        private readonly Guid _stubInitiatingUserId = Guid.NewGuid();
        private readonly DateTime _stubOperationCreatedOn = DateTime.Now;
        private readonly Guid _stubOperationId = Guid.NewGuid();
        private readonly Guid _stubOrganizationId = Guid.NewGuid();
        private Guid _stubPrimaryEntityId = Guid.NewGuid();
        private readonly Guid _stubRequestId = Guid.NewGuid();
        private ParameterCollection _inputParameters = new ParameterCollection();
        private ParameterCollection _outputParameters = new ParameterCollection();

        #endregion

        #region Properties

        protected StubITracingService TracingServiceStub { get; set; }

        protected StubIOrganizationServiceFactory OrganizationServiceFactoryStub { get; set; }

        /// <summary>
        /// Organisation Service using system account
        /// </summary>
        protected StubIOrganizationService SystemOrganizationServiceStub { get; set; }

        protected StubIOrganizationService OrganizationServiceStub { get; set; }

        protected StubIServiceProvider ServiceProviderStub { get; set; }

        protected StubIPluginExecutionContext PluginExecutionContextStub { get; set; }

        #endregion

        #region Constructors

        public PluginUnitTest()
        {
            Init();
        }

        #endregion

        #region Setup Stubs

        protected void Init()
        {
            PluginExecutionContextStub = new StubIPluginExecutionContext();
            TracingServiceStub = new StubITracingService();
            OrganizationServiceFactoryStub = new StubIOrganizationServiceFactory();
            SystemOrganizationServiceStub = new StubIOrganizationService();
            OrganizationServiceStub = new StubIOrganizationService();

            InitPluginExecutionContextMock(PluginExecutionContextStub);

            OrganizationServiceStub.CreateEntity = (entity) => Guid.NewGuid();

            SystemOrganizationServiceStub.CreateEntity = (entity) => Guid.NewGuid();

            OrganizationServiceFactoryStub.CreateOrganizationServiceNullableOfGuid = (id) =>
            {
                if (id == PluginExecutionContextStub.UserIdGet())
                    return OrganizationServiceStub;
                if (id == null)
                    return SystemOrganizationServiceStub;
                return new StubIOrganizationService();
            };

            ServiceProviderStub.GetServiceType = (type) =>
            {
                if (type == typeof(ITracingService))
                    return TracingServiceStub;
                if (type == typeof(IOrganizationServiceFactory))
                    return OrganizationServiceFactoryStub;
                if (type == typeof(IPluginExecutionContext))
                    return PluginExecutionContextStub;
                return null;
            };
        }

        protected void InitPluginExecutionContextMock(StubIPluginExecutionContext stubIPluginExecutionContext)
        {
            stubIPluginExecutionContext.BusinessUnitIdGet = () => _stubBusinessUnitId;
            stubIPluginExecutionContext.CorrelationIdGet = () => _stubCorrelationId;
            stubIPluginExecutionContext.DepthGet = () => 0;
            stubIPluginExecutionContext.InitiatingUserIdGet = () => _stubInitiatingUserId;
            stubIPluginExecutionContext.InputParametersGet = () => _inputParameters ?? (_inputParameters = new ParameterCollection());
            stubIPluginExecutionContext.OutputParametersGet = () => _outputParameters ?? (_outputParameters = new ParameterCollection());
            stubIPluginExecutionContext.IsExecutingOfflineGet = () => false;
            stubIPluginExecutionContext.IsInTransactionGet = () => false;
            stubIPluginExecutionContext.IsOfflinePlaybackGet = () => false;
            stubIPluginExecutionContext.IsolationModeGet = () => (int) PluginAssembyIsolationMode.Sandbox;
            stubIPluginExecutionContext.MessageNameGet = () => string.Empty;
            stubIPluginExecutionContext.ModeGet = () => (int) PluginMode.Synchronous;
            stubIPluginExecutionContext.OperationCreatedOnGet = () => _stubOperationCreatedOn;
            stubIPluginExecutionContext.OperationIdGet = () => _stubOperationId;
            stubIPluginExecutionContext.OrganizationIdGet = () => _stubOrganizationId;
            stubIPluginExecutionContext.OrganizationNameGet = () => string.Empty;
            stubIPluginExecutionContext.OwningExtensionGet = () => new EntityReference();
            stubIPluginExecutionContext.PostEntityImagesGet = () => new EntityImageCollection();
            stubIPluginExecutionContext.PreEntityImagesGet = () => new EntityImageCollection();
            stubIPluginExecutionContext.PrimaryEntityIdGet = Guid.NewGuid;
            stubIPluginExecutionContext.PrimaryEntityNameGet = () => string.Empty;
            stubIPluginExecutionContext.RequestIdGet = () => _stubRequestId;
            stubIPluginExecutionContext.SecondaryEntityNameGet = () => string.Empty;
            stubIPluginExecutionContext.SharedVariablesGet = () => new ParameterCollection();
            stubIPluginExecutionContext.UserIdGet = () => _stubContextUserId;
            stubIPluginExecutionContext.StageGet = () => (int) SdkMessageProcessingStepImage.PreValidation;
        }

        protected override IServiceProvider CreateServiceProvider() => ServiceProviderStub ?? (ServiceProviderStub = new StubIServiceProvider());

        #endregion

        #region Helper Methods

        protected override void SetPluginEvent(string primaryEntityName, string messageName, SdkMessageProcessingStepImage stage)
        {
            PluginExecutionContextStub.PrimaryEntityNameGet = () => primaryEntityName;
            PluginExecutionContextStub.MessageNameGet = () => messageName;
            PluginExecutionContextStub.StageGet = () => (int) stage;
        }

        #endregion
    }
}